package consultas;

import interfazGrafica.Grafo.Arista;
import interfazGrafica.Grafo.Punto;

import java.awt.Color;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.Stack;

import javax.swing.JOptionPane;

import estructuraGrafo.Enlace;
import estructuraGrafo.Grafo;
import estructuraGrafo.Nodo;

/**
 * Clase ConsultaC: Clase para realizar la consulta del item 5.E
 * 
 * @author  Grupo N11
 * @author  Prinsich Bernz, Emilio J. A.
 * @author      Solia, Guillermo D.
 * @version  1.0
 */

//Busqueda en Profundidad con n peajes
public class ConsultaE implements Serializable{

        private static final long serialVersionUID = 1L;
        /**Peajes*/
        private static int nPeajes;
        private static int pasa;
        /**Lista de Enlaces recoridos*/
        private static Set<Enlace> listaEnlaces;
        /**Lista de Nodos recorridos*/
        private static Set<Nodo> listaNodos;
        
        public ConsultaE(){
                listaEnlaces = new LinkedHashSet<Enlace>();
                listaNodos = new LinkedHashSet<Nodo>();
                nPeajes = -1;
                pasa = 0;
        }
        
        /**
         * A partir de una busqueda en profundidad recorre todos los nodos desde un punto si exceder una cuota
         * @param graph Garfo a Recorrer
         * @return Lista de Nodos Recorridos
         */
        public Set<Nodo> consultar(Grafo graph){

                String inicio = "#";
        		Object[] possibleValues = new Object[graph.getNombres().size()];
        		for(int i = 0; i < graph.getNombres().size(); i++)
        			possibleValues[i] = graph.getNombres().get(i);
        		
        		do{//Solicita al usuario que ingrese el nodo de inicio de la busqueda y verifica de que existan
        			inicio = (String) JOptionPane.showInputDialog(null, "Que tipo de nodo desea que sea?",
        					"Tipo de nodo", JOptionPane.INFORMATION_MESSAGE, null,
        					possibleValues, possibleValues[0]);
        			if(inicio.equals(""))
        				break;
        			else if(!graph.getNombres().contains(inicio)){
        				JOptionPane.showMessageDialog(null,"El peaje no existe, por favor ingrese otro","Peaje Inexistente", JOptionPane.ERROR_MESSAGE );
        				inicio = "#"; 
        			}
        		}while(inicio.equals("#"));
                
                do{//Solicita que ingrese la cantidad maxima de peajea por pasar y verifica que que no sea mayor a los nodos existentes                 
                        try     {
                                nPeajes = Integer.parseInt(JOptionPane.showInputDialog(null,"Ingrese el numero maximo de peaje por los caules desea pasar"));
                        }catch(NullPointerException e){return null;}
                         catch(Exception e){return null;}
                        if(nPeajes < 0){        
                                JOptionPane.showMessageDialog(null,"No puede pasar por peajes negatico","Error numero de peajes", JOptionPane.ERROR_MESSAGE );
                        }
                        if(nPeajes > graph.getNodos().size()){
                                JOptionPane.showMessageDialog(null,"No puede pasar por mas peajes de los que ya existen","Error numero de peajes", JOptionPane.ERROR_MESSAGE );
                                nPeajes = -1;
                        }
                }while(nPeajes < 0);
                
                pasa = nPeajes;
                nPeajes += 2;
                
                Nodo node = graph.getNodos().get(inicio);

                // cola con los nodos a visitar
                Stack<Nodo> aVisitar = new Stack<Nodo>();
                aVisitar.push(node);
                //realiza la busqueda por porfundidad sin excelder el numero de peajes ingresado
                while(!aVisitar.isEmpty()){
                        Nodo n = aVisitar.pop();
                        if(n.getNivel() <= pasa){
                        	Set<Nodo> adyacentes = listarAdyacentes(n, graph);
                        	for(Nodo adyacente: adyacentes ){
                        		if(!listaNodos.contains(adyacente)){
                        			aVisitar.push(adyacente);
                        		}
                        	}                       
                        	listaNodos.add(n);
                        }
                }
                
                return listaNodos;
        }
        
        /**
         * Lista los Nodos Adyasentes a un nodo
         * @param n Nodo
         * @param g Grafo
         * @return Lista de Nodos Adyasentes a n
         */
        private Set<Nodo>  listarAdyacentes(Nodo n, Grafo g) {
                
                Set<Nodo> listaAdyacentes = new LinkedHashSet<Nodo>();
                for(Enlace link : n.getEnlaces()){
                	if(link.isEstado()){ //agraga los nodo adyasentes sin exceder el maximo de peajes ingersado
                		listaAdyacentes.add(g.getNodos().get(link.getDestino()));
                		listaEnlaces.add(link);
                	}
                }
                return listaAdyacentes;
        }

        /**
         * Muesta en un String el resultado de la consulta E
         * @param graph Grafos
         * @return String de la consullta
         */
        public String getText(Grafo graph){
                Set<Nodo> node = consultar(graph);
                String tmp = "Pasando por " + pasa + " puede ir\nDesde: ";
                boolean t = true;
                for(Nodo n : node){
                        if(t){
                                if(n.getTipo() == 0)
                                        tmp = tmp + n.getNombre() + " - Acceso Norte\n>";
                                else
                                        tmp = tmp + n.getNombre() + "\nHasta los peajes: \n>";
                                t = false;
                        } else{
                                if(n.getTipo() == 1){
                                        tmp = tmp + n.getNombre() + " - Salida Sur\n";
                                        for(Enlace l : listaEnlaces)
                                                if(l.getDestino().equals(n.getNombre()))
                                                        tmp = tmp + "Por la Avenida: " + l.getNombre() + "\n";
                                        tmp = tmp + ">";
                                }
                                else{
                                        tmp = tmp + n.getNombre() + "\n";
                                        for(Enlace l : listaEnlaces)
                                                if(l.getDestino().equals(n.getNombre()))
                                                        tmp = tmp + "Por la Avenida: " + l.getNombre() + "\n";
                                        tmp = tmp + ">";
                                }
                        }
                }
                return tmp;
        }
        
        
        /**
         * Pinta el grafo que esta en el Linezo
         * @param aristas Arista que representan los enlaces
         * @param puntos Puntos que representan los nodos
         */
        public void pintar(ArrayList<Arista> aristas, ArrayList<Punto> puntos){
                boolean t = true;
                
                //Pinta las Aristas
                for(Enlace link : listaEnlaces){
                        for(Arista aris : aristas){
                                if(aris.getNombre().equals(link.getNombre())){
                                        aris.setColor(Color.RED);
                                }
                        }
                }
                //Pinta los Nodos
                for(Nodo node : listaNodos){
                        for(Punto point : puntos){
                                if(point.getNombre().equals(node.getNombre())){
                                        if(t){
                                                point.setColorPunto(Color.RED);
                                                t = false;
                                        }
                                        else
                                                point.setColorPunto(Color.GREEN);
                                }
                        }
                }
        }
}